package com.david.basic.dao;

import java.lang.reflect.ParameterizedType;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;

import com.david.basic.model.Pager;
import com.david.basic.model.SystemContext;

/**
 * @description 实现基类DAO
 * @author David
 * @version 2013-5-1
 * @param <T>
 */
@SuppressWarnings("unchecked")
public class BaseDao<T> implements IBaseDao<T> {
	private SessionFactory sessionFactory;

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	@Inject
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	/**
	 * 创建一个Class的对象来获取泛型的class
	 */
	private Class<T> clz;

	public Class<T> getClz() {
		if (clz == null) {
			// 获取泛型的class对象
			clz = (Class<T>) ((((ParameterizedType) (this.getClass().getGenericSuperclass()))
					.getActualTypeArguments()[0]));
		}
		return clz;
	}

	protected Session getSession() {
		return sessionFactory.getCurrentSession();
//		return sessionFactory.openSession();
	}

	public T add(T t) {
		getSession().save(t);
		return t;
	}

	public void delete(int t) {
		getSession().delete(this.load(t));
	}

	public Pager<T> find(String hql, Object[] args) {
		return this.find(hql, args, null);
	}

	public Pager<T> find(String hql, Object arg) {
		return this.find(hql, new Object[]{arg});
	}

	public Pager<T> find(String hql) {
		return this.find(hql, null);
	}
	/**
	 * 设置分页
	 * @param query
	 */
	private void setPagers(Query query,Pager pages){
		Integer pageSize = SystemContext.getPageSize();
		Integer pageOffset = SystemContext.getPageOffset();
		if(pageOffset == null || pageOffset < 0) pageOffset = 0;
		if(pageSize == null || pageSize < 0) pageSize = 15;
		pages.setOffset(pageOffset);
		pages.setSize(pageSize);
		query.setFirstResult(pageOffset).setMaxResults(pageSize);
	}
	/**
	 * 获取总的记录数
	 * @param hql
	 * @return
	 */
	private String getCountHql(String hql,boolean isHql){
		String endStr = hql.substring(hql.indexOf("from"));
		String str = "select count(*) "+endStr;
		if(isHql)
			str.replaceAll("fetch","");
		return str;
	}
	
	public Pager<T> find(String hql, Object[] args, Map<String, Object> alias) {
		hql = initSort(hql);
		String newStr = getCountHql(hql,true);
//		newStr = initSort(newStr);
		Query q = getSession().createQuery(newStr);
		Query query = getSession().createQuery(hql);
		//设置名句
		setAliasParameter(q, alias);
		setAliasParameter(query, alias);
		//设置参数
		setParameter(q, args);
		setParameter(query, args);
		Pager<T> pages = new Pager<T>();
		setPagers(query,pages);
		List<T> datas = query.list();
		pages.setDatas(datas);
		long total = (Long)q.uniqueResult();
		pages.setTotal(total);
		
		return pages;
	}

	public Pager<T> findByAlias(String hql, Map<String, Object> alias) {
		return this.find(hql, null, alias);
	}

	public <N extends Object>Pager<N> findBySql(String sql, Object[] args, Class<?> clz, boolean hasEntity) {
		return this.findBySql(sql, args, null, clz, hasEntity);
	}

	public <N extends Object>Pager<N> findBySql(String sql, Object arg, Class<?> clz, boolean hasEntity) {
		return this.findBySql(sql, new Object[]{arg}, clz, hasEntity);
	}

	public <N extends Object>Pager<N> findBySql(String sql, Class<?> clz, boolean hasEntity) {
		return this.findBySql(sql, null, clz, hasEntity);
	}

	public <N extends Object>Pager<N> findBySql(String sql, Object[] args, Map<String, Object> alias,
			Class<?> clz, boolean hasEntity) {
		sql = initSort(sql);
		String cq = getCountHql(sql, false);
		SQLQuery sq = getSession().createSQLQuery(sql);
		SQLQuery cqq = getSession().createSQLQuery(cq);
		setAliasParameter(sq, alias);
		setAliasParameter(cqq, alias);
		setParameter(sq, args);
		setParameter(cqq, args);
		Pager<N> pages = new Pager<N>();
		setPagers(sq, pages);
		if(hasEntity){
			sq.addEntity(clz);
		}else{
			sq.setResultTransformer(Transformers.aliasToBean(clz));
		}
		List<N> datas = sq.list();
		pages.setDatas(datas);
		long total = ((BigInteger)cqq.uniqueResult()).longValue();
		pages.setTotal(total);
		return pages;
	}

	public <N extends Object>Pager<N> findByAliasSql(String sql, Map<String, Object> alias, Class<?> clz,
			boolean hasEntity) {
		return this.findBySql(sql, null, alias, clz, hasEntity);
	}

	public List<T> list(String hql, Object[] args) {
		return this.list(hql, args, null);
	}

	public List<T> list(String hql, Object arg) {
		return this.list(hql, new Object[]{arg});
	}

	public List<T> list(String hql) {
		return this.list(hql,null);
	}

	/**
	 * 初始化排序
	 * @return
	 */
	private String initSort(String hql){
		String order = SystemContext.getOrder();
		String sort = SystemContext.getSort();
		//判断排序是否为空
		if(sort != null && !"".equals(sort.trim())){
			hql += " order by "+sort;
			if(!"desc".equals(order)) hql += " asc";
			else hql += " desc";
		}
		return hql;
	}
	/**
	 * 设置别名参数
	 * @param query
	 * @param alias
	 */
	private void setAliasParameter(Query query,Map<String, Object> alias){
		//判断是否有别名
		if(alias != null){
			Set<String> keys = alias.keySet();
			for(String key:keys){
				Object val = alias.get(key);
				if(val instanceof Collection){
					//设置到列表
					query.setParameterList(key, (Collection) val);
				}
			}
		}
	}
	
	/**
	 * 设置参数
	 * @param query
	 * @param args
	 */
	private void setParameter(Query query,Object[] args){
		//判断是否有参数
		if(args != null && args.length > 0){
			int index = 0;
			for(Object arg:args){
				query.setParameter(index++, arg);
			}
			
		}
	}
	
	public List<T> list(String hql, Object[] args, Map<String, Object> alias) {
		hql = initSort(hql);
		Query query = getSession().createQuery(hql);
		setAliasParameter(query, alias);
		setParameter(query, args);
		return query.list();
	}

	public List<T> listByAlias(String hql, Map<String, Object> alias) {
		return this.list(hql, null, alias);
	}

	public <N extends Object>List<N> listBySql(String sql, Object[] args, Class<?> clz, boolean hasEntity) {
		return this.listBySql(sql, args, null, clz, hasEntity);
	}

	public <N extends Object>List<N> listBySql(String sql, Object arg, Class<?> clz, boolean hasEntity) {
		return this.listBySql(sql, new Object[]{arg}, clz, hasEntity);
	}

	public <N extends Object>List<N> listBySql(String sql, Class<?> clz, boolean hasEntity) {
		return this.listBySql(sql, null, clz, hasEntity);
	}

	public <N extends Object>List<N> listBySql(String sql, Object[] args, Map<String, Object> alias,
			Class<?> clz, boolean hasEntity) {
		sql = initSort(sql);
		SQLQuery sq = getSession().createSQLQuery(sql);
		setAliasParameter(sq, alias);
		setParameter(sq, args);
		if(hasEntity){
			sq.addEntity(clz);
		}else
			sq.setResultTransformer(Transformers.aliasToBean(clz));
		return sq.list();
	}

	public <N extends Object>List<N> listByAliasSql(String sql, Map<String, Object> alias, Class<?> clz,
			boolean hasEntity) {
		return this.listBySql(sql, null, alias, clz, hasEntity);
	}

	
	public T load(int t) {
		return (T)getSession().load(getClz(), t);
	}

	public Object queryObjcet(String hql, Object[] args) {
		
		return this.queryObject(hql, args, null);
	}

	public Object queryObjcet(String hql, Object arg) {
		return this.queryObjcet(hql, new Object[]{arg});
	}

	public Object queryObjcet(String hql) {
		return this.queryObjcet(hql, null);
	}

	public void update(T t) {
		getSession().update(t);
	}

	public void updateByHql(String hql, Object[] args) {
		Query query = getSession().createQuery(hql);
		setParameter(query, args);
		query.executeUpdate();
	}

	public void updateByHql(String hql, Object arg) {
		this.updateByHql(hql, new Object[]{arg});
	}

	public void updateByHql(String hql) {
		this.updateByHql(hql,null);
	}

	public Object queryObject(String hql, Object[] args, Map<String, Object> alias) {
		Query query = getSession().createQuery(hql);
		setAliasParameter(query, alias);
		setParameter(query, args);
		return query.uniqueResult();
	}

	public Object queryObjectByAlias(String hql, Map<String, Object> alias) {
		return this.queryObject(hql, null, alias);
	}
	public Object queryObject(String hql) {
		return this.queryObject(hql,null);
	}
	
	public Object queryObject(String hql, Object arg) {
		return this.queryObject(hql, new Object[]{arg});
	}
	
	public Object queryObject(String hql, Object[] args) {
		return this.queryObject(hql, args,null);
	}

}
